package com.ms.common.util;

import com.ms.common.exception.BaseException;
import com.ms.common.exception.ExceptionCodeMark;
import com.ms.common.exception.ServiceException;
import com.ms.common.function.ArgsReturnableClosure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.regex.Pattern;
import java.util.zip.CRC32;

/**
 * @author zdh
 */
public class LangUtil {

    private static final Logger logger = LoggerFactory.getLogger(LangUtil.class);


    public final static long START_STMP = 1610374613027L; //2021-1-11 22:17的时间戳，用于某些基于时间戳算法的id减去基值

    public static final String UTF8 = "utf-8";
    public static final Pattern DIGIT = Pattern.compile("^[0-9]+$");
    public static final Pattern AWORD = Pattern.compile("^\\w+$", Pattern.CASE_INSENSITIVE);
    public static final String EMPTY_STRING = "";
    public static final Object EMPTY_OBJECT = new Object();
    public static final Object[] EMPTY_ARRAY = new Object[0];
    public static final String[] EMPTY_STRING_ARRAY = new String[0];
    public static final Class[] Empty_CLASSES = new Class[0];

    private final static Map<String, Pattern> REGEX_CACHE = new ConcurrentHashMap<>();

    private static final Collection<Class<?>> BASE_CLASS;
    private static final Collection<Class<?>> SIMPLE_CLASS;

    static {

        Set<Class<?>> cls = new HashSet<>();
        cls.add(Boolean.class);
        cls.add(boolean.class);
        cls.add(Character.class);
        cls.add(char.class);
        cls.add(Byte.class);
        cls.add(byte.class);
        cls.add(Short.class);
        cls.add(short.class);
        cls.add(Integer.class);
        cls.add(int.class);
        cls.add(Long.class);
        cls.add(long.class);
        cls.add(Float.class);
        cls.add(float.class);
        cls.add(Double.class);
        cls.add(double.class);
        BASE_CLASS = Collections.unmodifiableSet(cls);

        Set<Class<?>> simples = new HashSet<>(cls);
        simples.add(String.class);
        simples.add(Date.class);
        simples.add(Calendar.class);
        simples.add(Number.class);

        SIMPLE_CLASS = Collections.unmodifiableSet(simples);
    }

    public static final char[] WORD_CHARS = {'1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
            'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
            'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
            'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z'};


    public static TypeJudge FirstObject = new TypeJudge() {

        @Override
        public Object ifIterable(Iterable<?> obj) {
            Iterator<?> it = obj.iterator();
            if (it.hasNext()) {
                return it.next();
            }
            return null;
        }

        @Override
        public Object ifList(List list) {
            if (list.isEmpty()) {
                return null;
            }
            return list.get(0);
        }

        @Override
        public Object ifArray(Object[] array) {
            return array.length == 0 ? null : array[0];
        }

        @Override
        public Object ifCollection(Collection col) {
            if (col.isEmpty()) {
                return null;
            }
            return col.iterator().next();
        }

        @Override
        public Object other(Object obj, Class<?> type) {
            return obj;
        }

        @Override
        public Object ifMap(Map<?, ?> obj) {
            if (LangUtil.isEmpty(obj)) {
                return null;
            }
            return obj.entrySet().iterator().next().getValue();
        }

        @Override
        public Object all(Object obj) {
            return obj;
        }

    };

    public static TypeJudge NotNullObject = new TypeJudgeAdapter() {

        @Override
        public Object ifArray(Object[] array) {
            return EMPTY_ARRAY;
        }

        @Override
        public Object ifBoolean(Object obj) {
            return Boolean.FALSE;
        }

        @Override
        protected Object defaultValueIfOhter(Object obj) {
            return EMPTY_OBJECT;
        }

        @Override
        public Object ifCollection(Collection<?> obj) {
            return Collections.emptySet();
        }

        @Override
        public Object ifDouble(Object obj) {
            return Double.valueOf(0);
        }

        @Override
        public Object ifFloat(Object obj) {
            return Float.valueOf(0);
        }

        @Override
        public Object ifInteger(Object obj) {
            return Integer.valueOf(0);
        }

        @Override
        public Object ifList(List<?> obj) {
            return Collections.EMPTY_LIST;
        }

        @Override
        public Object ifLong(Object obj) {
            return Long.valueOf(0);
        }

        @Override
        public Object ifMap(Map<?, ?> obj) {
            return Collections.EMPTY_MAP;
        }

        @Override
        public Object ifShort(Object obj) {
            return Short.valueOf((short) 0);
        }

        @Override
        public Object ifString(Object obj) {
            return "";
        }

    };


    public static final double MB_SIZE = 1000.0 * 1000.0;
    public static final double KB_SIZE = 1000.0;
    private static final boolean debug = true;


    public static Collection<Class<?>> getBaseTypeClass() {
        return BASE_CLASS;
    }

    public static boolean isPrimitive(Class<?> clazz) {
        return clazz != null && clazz.isPrimitive();
    }

    public static boolean isBaseType(Class<?> clazz) {
        return BASE_CLASS.contains(clazz);
    }

    public static boolean isBaseTypeObject(Object obj) {
        if (obj == null) {
            return false;
        }
        return BASE_CLASS.contains(obj.getClass());
    }

    public static boolean isSimpleType(Class<?> clazz) {
        return SIMPLE_CLASS.contains(clazz);
    }

    public static boolean isSimpleTypeObject(Object obj) {
        if (obj == null) {
            return false;
        }
        return SIMPLE_CLASS.contains(obj.getClass());
    }

    public static Collection<Class<?>> getSimpleClass() {
        return SIMPLE_CLASS;
    }

    public static boolean isTimeClass(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        return Date.class.isAssignableFrom(clazz) || Calendar.class.isAssignableFrom(clazz);
    }

    public static boolean isTimeObject(Object obj) {
        if (obj == null) {
            return false;
        }
        Class clazz = obj.getClass();
        return Date.class.isAssignableFrom(clazz) || Calendar.class.isAssignableFrom(clazz);
    }

    public static boolean isMap(Object obj) {
        if (obj == null) {
            return false;
        }
        Class clazz = getObjectClass(obj);
        return Map.class.isAssignableFrom(clazz);
    }

    public static boolean isMapClass(Class clazz) {
        if (clazz == null) {
            return false;
        }
        return Map.class.isAssignableFrom(clazz);
    }

    public static boolean isCollectionClass(Class clazz) {
        if (clazz == null) {
            return false;
        }
        return Collection.class.isAssignableFrom(clazz);
    }

    public static boolean isIterableClass(Class clazz) {
        if (clazz == null) {
            return false;
        }
        return Iterable.class.isAssignableFrom(clazz);
    }

    public static boolean isArrayClass(Class clazz) {
        if (clazz == null) {
            return false;
        }
        return clazz.isArray();
    }

    public static Class getObjectClass(Object obj) {
        Class clazz = null;
        if (obj instanceof Class) {
            clazz = (Class) obj;
        } else {
            clazz = obj.getClass();
        }
        return clazz;
    }

    public static Throwable getCauseServiceException(Throwable e) {
        Throwable se;
        if (!(e instanceof BaseException) && e != null && e.getCause() != null) {
            se = getCauseServiceException(e.getCause());
        } else {
            se = e;
        }
        return se;
    }

    public static Throwable getFinalCauseException(Throwable e) {
        Throwable se = e;
        while (se.getCause() != null) {
            se = se.getCause();
        }
        return se;
    }

    public static <T extends Throwable> T getCauseException(Throwable e, Class<T> root) {
        Throwable se = e;
        while (se.getCause() != null) {
            se = se.getCause();
            if (root.isInstance(se)) {
                return (T) se;
            }
        }
        return null;
    }

    /***
     * 获取第一个不是jfish框架自定义异常的cause异常
     * @param e
     * @return
     */
    public static Throwable getFirstNotJFishThrowable(Throwable e) {
        Assert.notNull(e);
        Throwable se = e;
        while ((se = se.getCause()) != null) {
            if (!isJFishThrowable(se)) {
                return se;
            }
        }
        return se;
    }

    public static boolean isJFishThrowable(Throwable t) {
        return BaseException.class.isInstance(t) || ExceptionCodeMark.class.isInstance(t);
    }

    public static ServiceException asServiceException(Exception e) {
        return asServiceException(null, e);
    }

    public static ServiceException asServiceException(String msg, Exception e) {
        if (e instanceof ServiceException) {
            return (ServiceException) e;
        }
        ServiceException se = null;
        if (msg == null) {
            se = new ServiceException(e);
        } else {
            se = new ServiceException(msg, e);
        }
        return se;
    }

    public static void throwServiceException(Exception e) {
        throwServiceException(null, e);
    }

    public static void throwServiceException(String msg) {
        throwServiceException(msg, null);
    }

    public static void throwServiceException(String msg, Exception e) {
        throw asServiceException(msg, e);
    }

    public static void throwBaseException(Exception e) {
        throwBaseException(null, e);
    }

    public static void throwBaseException(String msg) {
        throw new BaseException(msg);
    }

    public static void throwBaseException(String msg, Exception e) {
        throw asBaseException(msg, e);
    }

    public static BaseException asBaseException(Exception e) {
        return asBaseException(null, e);
    }

    public static BaseException asBaseException(String msg) {
        return asBaseException(msg, null);
    }

    public static BaseException asBaseException(String msg, Throwable e) {
        if (e == null) {
            return new BaseException(msg);
        } else if (e instanceof BaseException) {
            return (BaseException) e;
        }

        BaseException se = null;
        if (msg == null) {
            se = new BaseException(e.getMessage(), e);
        } else {
            se = new BaseException(msg, e);
        }
        return se;
    }

    public static void throwIfNull(Object value, String msg) {
        if (value == null) {
            throwBaseException(msg);
        }
    }


    public static <T> T block(ArgsReturnableClosure block, Object... objects) {
        T result = null;
        try {
            result = block.execute(objects);
        } catch (Exception e) {
            throw asBaseException("block error: " + e.getMessage(), e);
        }
        return result;
    }

    public static void print(String str) {
        print(true, str);
    }

    public static void print(boolean print, String str) {
        if (print) {
            System.out.print(str);
        }
    }

    public static void println(String str, Object... objects) {
        println(true, str, objects);
    }

    public static void debug(String str, Object... objects) {
        str = "[DEBUG] " + str;
        println(debug, str, objects);
    }

    public static void println(boolean print, String str, Object...objects){
        toStringWith(print, str, false, objects);
    }

    public static String toStringWith(boolean print, String str, boolean named, Object...objects){
        StringBuilder sb = new StringBuilder();
        if(objects==null){
            sb.append(str).append("NULL");
            return sb.toString();
        }
        sb.append(str);
        for(Object obj : objects){
            sb.append("\t"+toString(obj));
        }
        return sb.toString();
    }


    public static void comment(String str, Object... objects) {
        comment(true, str, objects);
    }
    public static void comment(boolean print, String str, Object...objects){
        if(!print) {
            return ;
        }
        toStringWith(print, str, false, objects);
    }

    public static String toString(Throwable e, boolean detail) {
        Assert.notNull(e);

        if (!detail) {
            return e.getMessage();
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        String result = "";
        try {
            e.printStackTrace(pw);
            result = sw.toString();
        } finally {
            try {
                sw.close();
                pw.close();
            } catch (IOException ioe) {
                // ignore
            }
        }
        return result;
    }

    public static String toString(Object obj) {
        if (obj == null) {
            return "NULL";
        }
        StringBuilder result = new StringBuilder("");
        if (isBaseType(obj.getClass())) {
            result.append(obj);
        } else if (obj instanceof Class) {
            result.append(((Class) obj).getName());
        } else if (obj instanceof Date) {
            result.append(DateUtil.formatDateTime((Date) obj));
        } else if (obj instanceof String) {
            result.append(obj);
        } else if (obj instanceof Number) {
            result.append((obj));
        } else if (obj instanceof Map) {
            result.append("{");
            int index = 0;
            for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
                if (index != 0) {
                    result.append(", ");
                }
                result.append(toString(entry.getKey())).append(":").append(toString(entry.getValue()));
                index++;
            }
            result.append("}");
        } else if (obj instanceof Exception) {
            result.append(toString((Exception) obj, true));
        } else {
            Map props = ReflectUtils.toMap(obj);
            result.append(props.toString());
        }
        return result.toString();
    }


    public static String newString(byte[] bytes) {
        return newString(bytes, UTF8);
    }

    public static String newString(byte[] bytes, String charset) {
        String result = null;
        try {
            if (StringUtils.isBlank(charset)) {
                result = new String(bytes);
            } else {
                result = new String(bytes, charset);
            }
        } catch (UnsupportedEncodingException e) {
            throwBaseException(e);
        }
        return result;
    }


    public static String changeCharset(String str, String toCharset) {
        return changeCharset(str, null, toCharset);
    }

    public static String changeCharset(String str, String fromCharset, String toCharset) {
        String result = null;
        Assert.notNull(str);
        try {
            if (StringUtils.isBlank(fromCharset)) {
                result = new String(str.getBytes(), toCharset);
            } else {
                result = new String(str.getBytes(fromCharset), toCharset);
            }
        } catch (UnsupportedEncodingException e) {
            throwBaseException(e);
        }
        return result;
    }


    public static byte[] getBytes(String source) {
        return getBytes(source, UTF8);
    }

    public static byte[] getBytes(String source, String charset) {
        byte[] result = null;
        try {
            if (StringUtils.isBlank(charset)) {
                result = source.getBytes();
            } else {
                result = source.getBytes(charset);
            }
        } catch (UnsupportedEncodingException e) {
            throwBaseException(e);
        }
        return result;
    }


    private static final String HEX_CHAR = "0123456789ABCDEF";

    public static String toHex(byte[] bytes) {
        StringBuilder buf = new StringBuilder();
        for (byte b : bytes) {
            //a byte contains 8 bit, every 4 bit can indecated by a hex number, in other words, a byte(8 bit) can indecated by 2 hex number
            buf.append(HEX_CHAR.charAt((b >>> 4 & 0xf)));//high. symbol >>> is unsigned right shift
            buf.append(HEX_CHAR.charAt((b & 0xf)));//low
        }
        return buf.toString();
    }

    public static String toHex(byte b) {
        StringBuilder buf = new StringBuilder();
        buf.append(HEX_CHAR.charAt((b >>> 4 & 0xf)));//high. symbol >>> is unsigned right shift
        buf.append(HEX_CHAR.charAt((b & 0xf)));//low
        return buf.toString();
    }


    public static byte[] hex2Bytes(String str) {
        byte[] bytes = null;
        char[] chars = str.toUpperCase().toCharArray();

        int numb1;
        int numb2;
        for (int i = 0; i < chars.length; i = i + 2) {
            //every 2 hex number indecate a byte
            //每两个16进制数字 表示一个字节
            //第一个16进制数字表示高位
            numb1 = HEX_CHAR.indexOf(chars[i]) << 4 & 0xf0;//first hex number is high
            numb2 = HEX_CHAR.indexOf(chars[i + 1]) & 0xf;
            bytes = ArrayUtils.addAll(bytes, (byte) ((numb1 | numb2) & 0xff));
        }
        return bytes;
    }

    public static boolean isHexString(String str) {
        char[] chars = str.toUpperCase().toCharArray();
        int index = -1;
        for (char ch : chars) {
            index = HEX_CHAR.indexOf(ch);
            if (index == -1) {
                return false;
            }
        }
        return true;
    }


    public static String append(Object... strings) {
        return appendWith(false, strings);
    }

    public static String repeatString(int count, String op) {
        if (count < 1) {
            return "";
        }
        StringBuilder str = new StringBuilder(count * op.length());
        for (int i = 0; i < count; i++) {
            str.append(op);
        }
        return str.toString();
    }

    public static String appendNotBlank(Object... strings) {
        return appendWith(true, strings);
    }

    public static String appendWith(boolean ignoreBlank, Object... strings) {
        if (strings == null || strings.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (Object str : strings) {
            if (ignoreBlank) {
                if (str == null || StringUtils.isBlank(str.toString())) {
                    continue;
                }
            }
            sb.append(str);
        }
        return sb.toString();
    }

    public static String strings(String... strings) {
        if (strings == null || strings.length == 0) {
            return "";
        }
        int size = 0;
        for (String str : strings) {
            if (str == null) {
                continue;
            }
            size += str.length();
        }
        StringBuilder sb = new StringBuilder(size);
        for (String str : strings) {
            if (str == null) {
                continue;
            }
            sb.append(str);
        }
        return sb.toString();
    }


    public static int size(Object obj) {
        return size(obj, false);
    }

    public static int size(Object obj, boolean reflectSizeMethod) {
        if (obj == null) {
            return 0;
        }
        if (obj instanceof Collection) {
            return ((Collection) obj).size();
        } else if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length();
        } else if (obj instanceof Map) {
            return ((Map) obj).size();
        } else if (obj.getClass().isArray()) {
            return Array.getLength(obj);
        } else {
            if (reflectSizeMethod) {
                Method m = ReflectUtils.findMethod(true, obj.getClass(), "size");
                if (m != null) {
                    return (Integer) ReflectUtils.invokeMethod(m, obj);
                }
            }
            return 1;
        }
    }


    public static boolean isBlank(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof Iterable) {
            for (Object o : (Iterable) obj) {
                if (o != null) {
                    return false;
                }
            }
        } else if (obj instanceof CharSequence) {
            return  StringUtils.isBlank(obj.toString());
        } else if (obj instanceof Map) {
            for (Object o : ((Map) obj).values()) {
                if (o != null) {
                    return false;
                }
            }
        } else if (obj.getClass().isArray()) {
            int size = Array.getLength(obj);
            for (int i = 0; i < size; i++) {
                if (Array.get(obj, i) != null) {
                    return false;
                }
            }
        } else {
            return false;
        }
        return true;
    }

    public static boolean isMultiple(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof Iterable) {
            return true;
        } else if (obj.getClass().isArray()) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean isMultipleAndNotEmpty(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof Iterable) {
            Iterator<?> it = ((Iterable) obj).iterator();
            return it.hasNext();
        } else if (obj.getClass().isArray()) {
            return !isEmpty((Object[]) obj);
        } else {
            return false;
        }
    }

    public static boolean isMultipleObjectClass(Class clazz) {
        return isIterableClass(clazz) || isArrayClass(clazz);
    }

    public static boolean isArray(Object obj) {
        if (obj == null) {
            return false;
        }
        return obj.getClass().isArray();
    }

    public static Object judgeType(Object obj, TypeJudge executor) {
        Assert.notNull(obj);
        Class type = obj.getClass();
        return judgeType(obj, type, executor);
    }

    /********
     *
     * @param obj source object
     * @param type target class
     * @param executor
     * @return
     */
    public static Object judgeType(Object obj, Class type, TypeJudge executor) {
        Object result = executor.all(obj);
        if (Iterable.class.isAssignableFrom(type)) {
            result = executor.ifIterable((Iterable) obj);
        } else if (Collection.class.isAssignableFrom(type)) {
            if (List.class.isAssignableFrom(type)) {
                result = executor.ifList((List) obj);
            } else {
                result = executor.ifCollection((Collection) obj);
            }
        } else if (Map.class.isAssignableFrom(type)) {
            result = executor.ifMap((Map) obj);
        } else if (type.isArray()) {
            result = executor.ifArray((Object[]) obj);
        } else {
            result = executor.other(obj, type);
        }
        return result;
    }

    public static <T> T getFirst(Object obj) {
        if (obj == null) {
            return null;
        }
        return (T) judgeType(obj, FirstObject);
    }

    public static <T> Optional<T> getFirstOptional(Object obj) {
        if (obj == null) {
            return Optional.empty();
        }
        return Optional.ofNullable((T) judgeType(obj, FirstObject));
    }

    public static <T> T getFirstOrCreate(Object obj, Class<T> clazz) {
        if (obj == null) {
            return ReflectUtils.newInstance(clazz);
        }
        Object val = (T) judgeType(obj, FirstObject);
        return (T) (val == null ? ReflectUtils.newInstance(clazz) : val);
    }

    public static <T> List<T> emptyIfNull(List<T> list) {
        return list == null ? Collections.EMPTY_LIST : list;
    }

    public static <K, V> Map<K, V> emptyIfNull(Map<K, V> map) {
        return map == null ? Collections.EMPTY_MAP : map;
    }

    public static List defIfEmpty(List list, List def) {
        return emptyIfNull(list).isEmpty() ? def : list;
    }

    public static <T> T notNullValue(Object obj, Class<T> type) {
        if (obj != null) {
            return (T) obj;
        }
        return (T) judgeType(obj, type, NotNullObject);
    }


    public static String getRadomString(int length) {
        char[] result = new char[length];
        char[] takeArr = WORD_CHARS.clone();
        for (int i = 0, j = takeArr.length; i < length; ++i, --j) {
            int take = (int) (ThreadLocalRandom.current().nextDouble(1) * j);
            result[i] = takeArr[take];
            char m = takeArr[j - 1];
            takeArr[j - 1] = takeArr[take];
            takeArr[take] = m;
        }

        return new String(result);
    }

    public static String getRadomNumberString(int length) {
        int val = ThreadLocalRandom.current().nextInt((int) Math.pow(10, length));
        String value =  LangUtil.padLeft(String.valueOf(val), length, "0");
        return value;
    }



    private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance();


    public static void closeIO(Closeable io) {
        try {
            if (io != null) {
                io.close();
            }
        } catch (IOException e) {
            throwBaseException("close io error: " + e.getMessage(), e);
        }
    }


    public static boolean hasNotElement(Object obj) {
        return !hasElement(obj);
    }

    public static boolean hasElement(Object obj) {
        if (obj == null) {
            return false;
        } else if (CharSequence.class.isAssignableFrom(obj.getClass())) {
            return  StringUtils.isNotBlank(obj.toString());
        } else if (Collection.class.isAssignableFrom(obj.getClass())) {
            return hasElement((Collection) obj);
        } else if (obj.getClass().isArray()) {
            if (obj.getClass().getComponentType().isPrimitive()) {
                return Array.getLength(obj) != 0;
            } else {
                return hasElement((Object[]) obj);
            }
        } else if (Map.class.isAssignableFrom(obj.getClass())) {
            return hasElement((Map) obj);
        }
        return true;
    }

    public static boolean isEmpty(Object obj) {
        return size(obj) == 0;
    }


    public static boolean hasElement(Object[] obj) {
        for (Object o : obj) {
            if (o != null) {
                return true;
            }
        }
        return false;
    }

    public static boolean hasElement(Map map) {
        return !isEmpty(map);
    }

    public static boolean isEmpty(Map map) {
        return map == null || map.isEmpty();
    }

    public static boolean isNotEmpty(Map map) {
        return !isEmpty(map);
    }

    public static boolean hasElement(Collection col) {
        if (col == null || col.isEmpty()) {
            return false;
        }
        for (Object e : col) {
            if (e != null)//hasElement(e)
            {
                return true;
            }
        }
        return false;
    }

    public static boolean isEmpty(Collection col) {
        return (col == null || col.isEmpty());
    }


    public static boolean isEmpty(Object[] arrays) {
        return (arrays == null || arrays.length == 0);
    }

    public static boolean isNullOrEmptyObject(Object object) {
        if (object == null) {
            return true;
        }
        if (String.class.isInstance(object)) {
            return  StringUtils.isBlank((String) object);
        }
        if (object.getClass().isArray()) {
            return isEmpty((Object[]) object);
        } else if (Collection.class.isInstance(object)) {
            return isEmpty((Collection) object);
        } else if (Map.class.isInstance(object)) {
            return isEmpty((Map) object);
        }
        return false;
    }

    /**
     * 集合不为null ,且元素大于0个
     * @param col
     * @return
     */
    public static boolean isNotEmpty(Collection col) {
        return !isEmpty(col);
    }

    public static boolean equals(Object obj1, Object obj2) {
        return obj1 == obj2 || (obj1 != null && obj2 != null && obj1.equals(obj2));
    }

    public static boolean equalsIgnoreCase(String str1, String str2) {
        return str1 == str2 || (str1 != null && str2 != null && str1.equalsIgnoreCase(str2));
    }


    public static void await(long seconds) {
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));
        } catch (InterruptedException e) {
             LangUtil.throwBaseException("await error : " + e.getMessage(), e);
        }
    }

    public static void awaitInMillis(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
             LangUtil.throwBaseException("await error : " + e.getMessage(), e);
        }
    }

    public static boolean isIterable(Object object) {
        if (object == null) {
            return false;
        }

        if (object instanceof Map) {
            return true;
        } else if (object instanceof Iterable) {
            return true;
        } else if (object.getClass().isArray()) {
            return true;
        } else if (object instanceof Enumeration) {
            return true;
        } else if (object instanceof Iterator) {
            return true;
        } else {
            return false;
        }
    }

    public static Iterator convertIterator(Object value) {
        Iterator iterator;

        if (value instanceof Iterator) {
            return (Iterator) value;
        }

        if (value instanceof Map) {
            value = ((Map) value).entrySet();
        }

        if (value == null) {
            return null;
        }

        if (value instanceof Iterable) {
            iterator = ((Iterable) value).iterator();
        } else if (value.getClass().isArray()) {
            //need ability to support primitives; therefore, cannot
            //use Object[] casting.
            ArrayList list = new ArrayList(Array.getLength(value));

            for (int j = 0; j < Array.getLength(value); j++) {
                list.add(Array.get(value, j));
            }

            iterator = list.iterator();
        } else if (value instanceof Enumeration) {
            Enumeration enumeration = (Enumeration) value;
            ArrayList list = new ArrayList();

            while (enumeration.hasMoreElements()) {
                list.add(enumeration.nextElement());
            }

            iterator = list.iterator();
        } else {
            List list = new ArrayList(1);
            list.add(value);
            iterator = list.iterator();
        }

        return iterator;
    }

    public static <T> T fetchNotNull(T... args) {
        Assert.notNull(args);
        T val = null;
        for (T obj : args) {
            if (obj instanceof String) {
                if ( StringUtils.isNotBlank((String) obj)) {
                    val = obj;
                    break;
                }
            } else {
                if (obj != null) {
                    val = obj;
                    break;
                }
            }
        }
        return val;
    }



    /*********
     * map contains a value which instance of this clazz
     * @param model
     * @param clazz
     * @return
     */
    public static boolean isMapContainsInstance(Map<String, Object> model, Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        for (Object val : model.values()) {
            if (clazz.isInstance(val)) {
                return true;
            }
        }
        return false;
    }

    public static void remove(Collection<?> collection, int start, int end) {
        Assert.notEmpty(collection);
        if (end > collection.size()) {
            end = collection.size();
        }
        Iterator<?> it = collection.iterator();
        for (int i = 0; it.hasNext(); i++) {
            if (i >= end) {
                break;
            }
            it.next();
            if (i >= start) {
                it.remove();
            }
        }
    }

    public static Map<String, String> toMap(Properties prop) {
        Assert.notNull(prop);
        Map<String, String> map = new HashMap<>();
        Enumeration<String> keys = (Enumeration<String>) prop.propertyNames();
        String name = null;
        while (keys.hasMoreElements()) {
            name = keys.nextElement();
            map.put(name, prop.getProperty(name));
        }
        return map;
    }



    public static <T> List<T> asListWithType(Class<T> clazz, Object... objects) {
        if (LangUtil.isEmpty(objects)) {
            return Collections.EMPTY_LIST;
        }
        List<T> list = new ArrayList<T>(objects.length);
        for (Object obj : objects) {
            if (clazz.isInstance(obj)) {
                list.add((T) obj);
            }
        }
        return list;
    }

    public static String getBaseExceptonCode(Exception e) {
        if (ExceptionCodeMark.class.isInstance(e)) {
            return ((ExceptionCodeMark) e).getCode();
        }
        return EMPTY_STRING;
    }

    public static boolean isError(Exception e, String errorCode) {
        String code = getBaseExceptonCode(e);
        return code.equals(errorCode);
    }

    public static boolean matche(boolean cache, String regex, String str) {
        Pattern pattern = null;
        if (cache) {
            pattern = REGEX_CACHE.get(regex);
            if (pattern == null) {
                pattern = Pattern.compile(regex);
                REGEX_CACHE.put(regex, pattern);
            }
        } else {
            pattern = Pattern.compile(regex);
        }
        return pattern.matcher(str).matches();
    }

   
    public static boolean isWord(String str) {
        return AWORD.matcher(str).matches();
    }

    public static Object formatValue(Object value, String dataFormat) {
        Object actualValue;
        if (value instanceof Date) {
            if ( StringUtils.isBlank(dataFormat)) {
                dataFormat = DateUtil.DATE_TIME;
            }
            actualValue = DateUtil.format(dataFormat, (Date) value);
        } else if (value instanceof Number && dataFormat != null) {
            NumberFormat nf = new DecimalFormat(dataFormat);
            nf.setRoundingMode(RoundingMode.HALF_UP);
            actualValue = nf.format(value);
        } else {
            actualValue = value;
        }
        return actualValue;
    }

    public static BigDecimal roundHalfUp(BigDecimal number) {
        number.setScale(2, BigDecimal.ROUND_HALF_UP); // 四色五入，保持两个小数点
        return number;
    }

    public static String format(Number num, String pattern) {
        NumberFormat format = new DecimalFormat(pattern);
        return format.format(num);
    }

    public static String format(Number num) {
        return new DecimalFormat("0.00").format(num);
    }

    /***
     * 如果字符串超过指定长度，则填充省略号
     * @author weishao zeng
     * @param str
     * @param length
     * @return
     */
    public static String ellipsis(String str, int length) {
        if ( StringUtils.isBlank(str)) {
            return str;
        }
        if (str.length() <= length) {
            return str;
        }
        String newStr = str.substring(0, length - 3) + "...";
        return newStr;
    }

    /*****
     * 填充字符串，如果s的长度少于taotalLength，则在左边填充(aleng-s.length)个append
     * @param s
     * @param taotalLength
     * @param append
     * @return
     */
    public static String padLeft(String s, int taotalLength, String append) {
        return pad(s, taotalLength, append.charAt(0), true);
    }

    public static String padRight(String s, int taotalLength, String append) {
        return pad(s, taotalLength, append.charAt(0), false);
    }

    public static String pad(String s, int taotalLength, char append, boolean padLeft) {
        Assert.isTrue(taotalLength > 0, "total length must be >0");
        if (s == null) {
            return s;
        }
        int length = Math.abs(taotalLength);
        if (s.length() == length) {
            return s;
        }
        StringBuilder str = new StringBuilder(s);
        if (str.length() < length) {
            int lack = length - str.length();
            char[] appendChars = new char[lack];
            Arrays.fill(appendChars, append);
            if (padLeft) {
                str.insert(0, appendChars);
            } else {
                str.append(appendChars);
            }
        } else {
            if (taotalLength > 0) {
                str.delete(length, str.length());
            } else {
                str.delete(0, str.length() - length);
            }
        }
        return str.toString();
    }

    public static String fixedLengthString(String str, int length, String padString) {
        if ( StringUtils.isBlank(str)) {
            return padLeft("", length, padString);
        }
        if (str.length() >= length) {
            return str.substring(str.length() - length);
        } else {
            return padLeft(str, length, padString);
        }
    }


    public static Object safeGetValue(Object elemetns, int index) {
        return safeGetValue(elemetns, index, null);
    }

    public static Object safeGetValue(Object elemetns, int index, Object def) {
        if (elemetns == null) {
            return def;
        }
        if (elemetns.getClass().isArray()) {
            Object[] array = (Object[]) elemetns;
            if (index >= array.length) {
                return def;
            }
            return array[index];
        } else if (List.class.isInstance(elemetns)) {
            List<?> list = (List) elemetns;
            if (index >= list.size()) {
                return def;
            }
            return list.get(index);
        } else {
            throw new UnsupportedOperationException("unsupported type: " + elemetns.getClass());
        }
    }

    public static Long hexToLong(String hexStr) {
        return Long.parseLong(hexStr, 16);
    }

    public static String encodeUrl(String url) {
        try {
            return URLEncoder.encode(url, UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new BaseException("Unsupported Encoding", e);
        }
    }

    public static String decodeUrl(String url) {
        try {
            return URLDecoder.decode(url, UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new BaseException("Unsupported Encoding", e);
        }
    }


    public static String safeUrlEncode(String base64) {
        return safeUrlEncode(base64, false);
    }

    /***
     * URL安全的Base64编码适用于以URL方式传递Base64编码结果的场景。该编码方式的基本过程是先将内容以Base64格式编码为字符串，然后检查该结果字符串，将字符串中的加号+换成中划线-，并且将斜杠/换成下划线_。
     详细编码规范请参考RFC4648标准中的相关描述。
     补充：对于末尾的“=”占位符，Bouncy Castle将之用.代替，而Commons Codes杜绝任何的补位符。
     * @author weishao zeng
     * @param base64
     * @return
     */
    public static String safeUrlEncode(String base64, boolean bouncyCastle) {
        String val = base64;
        val = val.replace('+', '-');
        val = val.replace('/', '_');
        if (bouncyCastle) {
            val = val.replace('=', '.');
        } else {
            val = val.replace("=", "");
        }
        return val;
    }

    public static String safeUrldecode(String base64, boolean bouncyCastle) {
        String val = base64;
        val = val.replace('-', '+');
        val = val.replace('_', '/');
        if (bouncyCastle) {
            val = val.replace('.', '=');
        } else {
            int mod4 = base64.length() % 4;
            if (mod4 > 0) {
                val = val + "====".substring(mod4);
            }
        }
        return val;
    }

    public static boolean isDigitString(String str) {
        return DIGIT.matcher(str).matches();
    }

    public static <T> T cast(Object obj, Class<T> clazz) {
        return clazz.cast(obj);
    }

    public static Object firstNotNull(Object... objects) {
        for (Object obj : objects) {
            if (obj != null) {
                return obj;
            }
        }
        return null;
    }

    public static <T> T[] makeGenericArray(Class<T> cls, int length) {
        return (T[]) Array.newInstance(cls, length);
    }

    public static void lockAction(Lock lock, Runnable action) {
        lock.lock();
        try {
            action.run();
        } finally {
            lock.unlock();
        }
    }



    public static boolean isNumberObject(Object val) {
        if (val == null) {
            return false;
        }
        return isNumberType(val.getClass());
    }

    public static boolean isNumberType(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        if (clazz.isPrimitive()) {
            return int.class == clazz || long.class == clazz || short.class == clazz || float.class == clazz || double.class == clazz;
        }
        return Number.class.isAssignableFrom(clazz);
    }

    public static boolean isIntegralType(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        if (clazz.isPrimitive()) {
            return int.class == clazz || long.class == clazz || short.class == clazz;
        }
        return Integer.class == clazz || Long.class == clazz || Short.class == clazz;
    }

    public static Optional<String> getPid() {
        try {
            String jvmName = ManagementFactory.getRuntimeMXBean().getName();
            return Optional.ofNullable(jvmName.split("@")[0]);
        } catch (Throwable ex) {
            return Optional.empty();
        }
    }

    public static String getOsName() {
        return System.getProperties().getProperty("os.name", "");
    }

    public static boolean isWindowsOS() {
        return getOsName().toLowerCase().startsWith("win");
    }

    public static String converTo36Radix(String str) {
        Long value = getCrc32(str.getBytes());
        return Long.toString(value, 36);
    }

    public static long getCrc32(String str) {
        return getCrc32(str.getBytes());
    }

    public static long getCrc32(byte[] bytes) {
        CRC32 crc = new CRC32();
        crc.update(bytes);
        return crc.getValue();
    }

    public static String randomUUID() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static long size(File...files){
        Assert.notEmpty(files);
        long size = 0;
        for(File f : files){
            size += f.length();
        }
        return size;
    }

}
